"""
文件名: user_service.py
描述: 用户管理服务

本模块包含以下主要功能：
1. 用户数据的CRUD操作
2. 用户订阅频道的同步和管理
3. 用户统计信息更新
4. 用户权限验证
5. 用户数据的聚合查询

依赖模块:
   - sqlalchemy: 数据库操作
   - datetime: 时间处理
   - typing: 类型注解
   - uuid: 唯一标识符

使用示例:
   >>> user_service = UserService()
   >>> user = await user_service.get_user_by_id(user_id)
   >>> await user_service.sync_user_subscriptions(user, credentials)
   >>> stats = await user_service.get_user_stats(user_id)

注意事项:
   - 用户敏感信息要加密存储
   - 数据库操作要有事务支持
   - 批量操作要考虑性能影响
   - 用户删除采用软删除
   - 要处理并发更新冲突

作者: AI助手
创建日期: 2024-06-27
最后修改: 2024-06-27
版本: 1.0.0
"""

from datetime import datetime, timedelta
from typing import Dict, List, Optional, Tuple, Any
import uuid

from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, update, delete, func, and_, or_
from sqlalchemy.orm import selectinload, joinedload
from google.oauth2.credentials import Credentials

from src.core.database import get_db_session
from src.core.redis import redis_manager
from src.core.logger import get_logger
from src.models.user import User, UserCreate, UserUpdate, UserInDB, UserStats
from src.models.channel import Channel, ChannelCreate, ChannelUpdate
from src.services.youtube_service import YouTubeService
from src.utils.security import encrypt_token, decrypt_token
from src.utils.exceptions import (
    DatabaseException,
    ValidationException,
    YouTubeAPIException,
    AuthenticationException,
)

logger = get_logger(__name__)


class UserService:
    """用户管理服务类"""
    
    def __init__(self):
        """初始化用户服务"""
        self.redis = redis_manager
        self.youtube_service = YouTubeService()
    
    async def get_user_by_id(self, user_id: uuid.UUID) -> Optional[User]:
        """
        根据ID获取用户
        
        Args:
            user_id: 用户ID
            
        Returns:
            Optional[User]: 用户对象，未找到返回None
        """
        try:
            async with get_db_session() as db:
                stmt = select(User).where(
                    User.id == user_id,
                    User.deleted_at.is_(None)
                )
                result = await db.execute(stmt)
                user = result.scalar_one_or_none()
                
                return user
                
        except Exception as e:
            logger.error(f"根据ID获取用户失败: {e}", user_id=str(user_id))
            raise DatabaseException(f"获取用户失败: {str(e)}")
    
    async def get_user_by_google_id(self, google_id: str) -> Optional[User]:
        """
        根据Google ID获取用户
        
        Args:
            google_id: Google用户ID
            
        Returns:
            Optional[User]: 用户对象，未找到返回None
        """
        try:
            async with get_db_session() as db:
                stmt = select(User).where(
                    User.google_id == google_id,
                    User.deleted_at.is_(None)
                )
                result = await db.execute(stmt)
                user = result.scalar_one_or_none()
                
                return user
                
        except Exception as e:
            logger.error(f"根据Google ID获取用户失败: {e}", google_id=google_id)
            raise DatabaseException(f"获取用户失败: {str(e)}")
    
    async def get_user_by_email(self, email: str) -> Optional[User]:
        """
        根据邮箱获取用户
        
        Args:
            email: 用户邮箱
            
        Returns:
            Optional[User]: 用户对象，未找到返回None
        """
        try:
            async with get_db_session() as db:
                stmt = select(User).where(
                    User.email == email.lower(),
                    User.deleted_at.is_(None)
                )
                result = await db.execute(stmt)
                user = result.scalar_one_or_none()
                
                return user
                
        except Exception as e:
            logger.error(f"根据邮箱获取用户失败: {e}", email=email)
            raise DatabaseException(f"获取用户失败: {str(e)}")
    
    async def create_user(self, user_data: UserCreate) -> User:
        """
        创建新用户
        
        Args:
            user_data: 用户创建数据
            
        Returns:
            User: 创建的用户对象
        """
        try:
            async with get_db_session() as db:
                # 检查用户是否已存在
                existing_user = await self.get_user_by_google_id(user_data.google_id)
                if existing_user:
                    raise ValidationException(f"用户已存在: {user_data.google_id}")
                
                # 创建用户
                user_dict = user_data.dict()
                
                # 加密敏感信息
                if user_dict.get("access_token"):
                    user_dict["access_token"] = encrypt_token(user_dict["access_token"])
                if user_dict.get("refresh_token"):
                    user_dict["refresh_token"] = encrypt_token(user_dict["refresh_token"])
                
                user = User(**user_dict)
                user.created_at = datetime.utcnow()
                user.updated_at = datetime.utcnow()
                
                db.add(user)
                await db.commit()
                await db.refresh(user)
                
                logger.info(f"用户创建成功", user_id=str(user.id), email=user.email)
                return user
                
        except ValidationException:
            raise
        except Exception as e:
            logger.error(f"创建用户失败: {e}", user_data=user_data.dict())
            raise DatabaseException(f"创建用户失败: {str(e)}")
    
    async def update_user(self, user_id: uuid.UUID, user_data: UserUpdate) -> Optional[User]:
        """
        更新用户信息
        
        Args:
            user_id: 用户ID
            user_data: 更新数据
            
        Returns:
            Optional[User]: 更新后的用户对象，未找到返回None
        """
        try:
            async with get_db_session() as db:
                # 查找用户
                stmt = select(User).where(
                    User.id == user_id,
                    User.deleted_at.is_(None)
                )
                result = await db.execute(stmt)
                user = result.scalar_one_or_none()
                
                if not user:
                    return None
                
                # 更新字段
                update_dict = user_data.dict(exclude_unset=True)
                
                # 加密敏感信息
                if "access_token" in update_dict and update_dict["access_token"]:
                    update_dict["access_token"] = encrypt_token(update_dict["access_token"])
                if "refresh_token" in update_dict and update_dict["refresh_token"]:
                    update_dict["refresh_token"] = encrypt_token(update_dict["refresh_token"])
                
                # 更新字段
                for field, value in update_dict.items():
                    if hasattr(user, field):
                        setattr(user, field, value)
                
                user.updated_at = datetime.utcnow()
                
                await db.commit()
                await db.refresh(user)
                
                logger.info(f"用户信息更新成功", user_id=str(user.id))
                return user
                
        except Exception as e:
            logger.error(f"更新用户失败: {e}", user_id=str(user_id))
            raise DatabaseException(f"更新用户失败: {str(e)}")
    
    async def soft_delete_user(self, user_id: uuid.UUID) -> bool:
        """
        软删除用户
        
        Args:
            user_id: 用户ID
            
        Returns:
            bool: 是否删除成功
        """
        try:
            async with get_db_session() as db:
                stmt = update(User).where(
                    User.id == user_id,
                    User.deleted_at.is_(None)
                ).values(
                    deleted_at=datetime.utcnow(),
                    is_active=False,
                    updated_at=datetime.utcnow()
                )
                
                result = await db.execute(stmt)
                await db.commit()
                
                success = result.rowcount > 0
                if success:
                    logger.info(f"用户软删除成功", user_id=str(user_id))
                else:
                    logger.warning(f"用户软删除失败，用户不存在", user_id=str(user_id))
                
                return success
                
        except Exception as e:
            logger.error(f"软删除用户失败: {e}", user_id=str(user_id))
            raise DatabaseException(f"删除用户失败: {str(e)}")
    
    async def get_user_credentials(self, user: User) -> Optional[Credentials]:
        """
        获取用户的YouTube API认证凭据
        
        Args:
            user: 用户对象
            
        Returns:
            Optional[Credentials]: OAuth认证凭据，失败返回None
        """
        try:
            if not user.access_token or not user.refresh_token:
                logger.warning(f"用户缺少OAuth令牌", user_id=str(user.id))
                return None
            
            # 解密令牌
            access_token = decrypt_token(user.access_token)
            refresh_token = decrypt_token(user.refresh_token)
            
            # 检查令牌是否过期
            if user.is_token_expired:
                logger.info(f"用户访问令牌已过期，尝试刷新", user_id=str(user.id))
                
                # 刷新令牌
                new_credentials = await self.youtube_service.refresh_user_credentials(refresh_token)
                if new_credentials:
                    # 更新用户令牌
                    await self.update_user(user.id, UserUpdate(
                        access_token=new_credentials.token,
                        refresh_token=new_credentials.refresh_token,
                        token_expires_at=new_credentials.expiry,
                    ))
                    
                    return new_credentials
                else:
                    logger.error(f"刷新用户令牌失败", user_id=str(user.id))
                    return None
            
            # 创建认证凭据对象
            credentials = Credentials(
                token=access_token,
                refresh_token=refresh_token,
                token_uri="https://oauth2.googleapis.com/token",
                client_id=user.google_id,  # 这里使用google_id，实际应该是client_id
            )
            
            return credentials
            
        except Exception as e:
            logger.error(f"获取用户认证凭据失败: {e}", user_id=str(user.id))
            return None
    
    async def sync_user_subscriptions(self, user: User) -> Dict[str, Any]:
        """
        同步用户的YouTube订阅频道
        
        Args:
            user: 用户对象
            
        Returns:
            Dict[str, Any]: 同步结果统计
        """
        try:
            # 获取用户认证凭据
            credentials = await self.get_user_credentials(user)
            if not credentials:
                raise AuthenticationException("无法获取用户认证凭据")
            
            # 从YouTube API获取订阅列表
            subscriptions = await self.youtube_service.get_user_subscriptions(
                credentials, max_results=1000
            )
            
            # 同步统计
            stats = {
                "total_subscriptions": len(subscriptions),
                "new_channels": 0,
                "updated_channels": 0,
                "failed_channels": 0,
                "sync_time": datetime.utcnow(),
            }
            
            async with get_db_session() as db:
                for subscription in subscriptions:
                    try:
                        snippet = subscription.get("snippet", {})
                        resource_id = snippet.get("resourceId", {})
                        channel_id = resource_id.get("channelId")
                        
                        if not channel_id:
                            stats["failed_channels"] += 1
                            continue
                        
                        # 检查频道是否已存在
                        stmt = select(Channel).where(Channel.channel_id == channel_id)
                        result = await db.execute(stmt)
                        existing_channel = result.scalar_one_or_none()
                        
                        if existing_channel:
                            # 更新现有频道
                            await self._update_channel_from_subscription(
                                db, existing_channel, subscription
                            )
                            stats["updated_channels"] += 1
                        else:
                            # 创建新频道
                            await self._create_channel_from_subscription(
                                db, subscription
                            )
                            stats["new_channels"] += 1
                    
                    except Exception as e:
                        logger.error(f"同步频道失败: {e}", channel_data=subscription)
                        stats["failed_channels"] += 1
                        continue
                
                # 更新用户统计信息
                user.total_channels = stats["total_subscriptions"]
                user.last_sync_at = stats["sync_time"]
                user.updated_at = datetime.utcnow()
                
                await db.commit()
            
            logger.info(f"用户订阅同步完成", user_id=str(user.id), stats=stats)
            return stats
            
        except AuthenticationException:
            raise
        except Exception as e:
            logger.error(f"同步用户订阅失败: {e}", user_id=str(user.id))
            raise YouTubeAPIException(f"同步订阅失败: {str(e)}")
    
    async def _create_channel_from_subscription(
        self, 
        db: AsyncSession, 
        subscription: Dict[str, Any]
    ) -> Channel:
        """
        从订阅数据创建频道
        
        Args:
            db: 数据库会话
            subscription: 订阅数据
            
        Returns:
            Channel: 创建的频道对象
        """
        snippet = subscription.get("snippet", {})
        resource_id = snippet.get("resourceId", {})
        
        channel_data = ChannelCreate(
            channel_id=resource_id.get("channelId"),
            title=snippet.get("title", ""),
            description=snippet.get("description", ""),
            thumbnail_url=snippet.get("thumbnails", {}).get("default", {}).get("url"),
            sync_status="pending",
        )
        
        channel = Channel(**channel_data.dict())
        channel.created_at = datetime.utcnow()
        channel.updated_at = datetime.utcnow()
        
        db.add(channel)
        await db.flush()  # 获取ID但不提交
        
        return channel
    
    async def _update_channel_from_subscription(
        self,
        db: AsyncSession,
        channel: Channel,
        subscription: Dict[str, Any]
    ) -> None:
        """
        从订阅数据更新频道
        
        Args:
            db: 数据库会话
            channel: 频道对象
            subscription: 订阅数据
        """
        snippet = subscription.get("snippet", {})
        
        # 更新基本信息
        channel.title = snippet.get("title", channel.title)
        channel.description = snippet.get("description", channel.description)
        
        # 更新缩略图
        thumbnails = snippet.get("thumbnails", {})
        if thumbnails:
            # 优先使用高质量缩略图
            for quality in ["high", "medium", "default"]:
                if quality in thumbnails:
                    channel.thumbnail_url = thumbnails[quality].get("url")
                    break
        
        channel.updated_at = datetime.utcnow()
    
    async def get_user_stats(self, user_id: uuid.UUID) -> Optional[UserStats]:
        """
        获取用户统计信息
        
        Args:
            user_id: 用户ID
            
        Returns:
            Optional[UserStats]: 用户统计信息，未找到返回None
        """
        try:
            async with get_db_session() as db:
                # 获取用户基本信息
                user_stmt = select(User).where(
                    User.id == user_id,
                    User.deleted_at.is_(None)
                )
                user_result = await db.execute(user_stmt)
                user = user_result.scalar_one_or_none()
                
                if not user:
                    return None
                
                # 获取缓存视频数量（这里暂时设为0，后续实现视频模块时更新）
                total_videos_cached = 0
                
                # 确定同步状态
                sync_status = "never"
                if user.last_sync_at:
                    time_since_sync = datetime.utcnow() - user.last_sync_at
                    if time_since_sync.total_seconds() < 3600:  # 1小时内
                        sync_status = "recent"
                    elif time_since_sync.total_seconds() < 86400:  # 24小时内
                        sync_status = "outdated"
                    else:
                        sync_status = "old"
                
                stats = UserStats(
                    total_channels=user.total_channels,
                    total_categories=user.total_categories,
                    total_videos_cached=total_videos_cached,
                    last_sync_at=user.last_sync_at,
                    sync_status=sync_status,
                )
                
                return stats
                
        except Exception as e:
            logger.error(f"获取用户统计信息失败: {e}", user_id=str(user_id))
            raise DatabaseException(f"获取统计信息失败: {str(e)}")
    
    async def get_active_users_count(self) -> int:
        """
        获取活跃用户数量
        
        Returns:
            int: 活跃用户数量
        """
        try:
            async with get_db_session() as db:
                stmt = select(func.count(User.id)).where(
                    User.is_active == True,
                    User.deleted_at.is_(None)
                )
                result = await db.execute(stmt)
                count = result.scalar()
                
                return count or 0
                
        except Exception as e:
            logger.error(f"获取活跃用户数量失败: {e}")
            return 0
    
    async def get_users_need_sync(self, limit: int = 100) -> List[User]:
        """
        获取需要同步的用户列表
        
        Args:
            limit: 最大返回数量
            
        Returns:
            List[User]: 需要同步的用户列表
        """
        try:
            async with get_db_session() as db:
                # 查找超过1小时未同步的活跃用户
                sync_threshold = datetime.utcnow() - timedelta(hours=1)
                
                stmt = select(User).where(
                    and_(
                        User.is_active == True,
                        User.deleted_at.is_(None),
                        or_(
                            User.last_sync_at.is_(None),
                            User.last_sync_at < sync_threshold
                        )
                    )
                ).limit(limit)
                
                result = await db.execute(stmt)
                users = result.scalars().all()
                
                return list(users)
                
        except Exception as e:
            logger.error(f"获取需要同步的用户失败: {e}")
            return []


# 导出主要接口
__all__ = [
    "UserService",
]